import subprocess
import pickle
import random
import sys
import socket
import smtplib
import mimetypes
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.MIMEText import MIMEText
from email.MIMEAudio import MIMEAudio
from email.MIMEImage import MIMEImage
from email.Encoders import encode_base64
import string
import os #not necassary but later on I am going to use a few features from this
import sys,xmpp, time,asynchat,asyncore
from configobj import ConfigObj
config = ConfigObj('hans.conf')
# output_file=open("hans.stdout.txt","w")
# output_file.flush()

HOST=config['HOST'] #The server we want to connect to
PORT=int(config['PORT']) # 6667 #The connection port which is usually 6667
NICK=config['NICK'] #The bot's nickname
IDENT=config['IDENT']
REALNAME=config['REALNAME']
OWNER=config['OWNER'] #The bot owner's nick
CHANNELINIT=config['CHANNELINIT'] #The default channel for the bot
TRIGGERTEXT=config['TRIGGERTEXT']

# Google Talk constants
FROM_GMAIL_ID = config['FROM_GMAIL_ID']
GMAIL_PASS = config['GMAIL_PASS']
GTALK_SERVER = config['GTALK_SERVER']
admin = config['admin']
priority_map = {'unavailable': 'none', 'dnd': 'priority', 'away': 'all', 'available': 'all'} 
ping_timeout = 5
class Member:
    def __init__(self, e_mail):
        self.user=e_mail
        self.name = e_mail.split('@')[0]
        self.timestamp=time.time()
        self.status = {}
        self.type ={}
        self.show = {}
	self.show_time={}

    def send_message(self, msg):
        print self.user
        # output_file.flush()
        print self.show
        # output_file.flush()
        print self.status
        # output_file.flush()
        results = False
  
        if len(self.show) == 0:
            return True
        for er, s in self.status.items():
            if s.find("dnd" )  ==   - 1:
                return True

            return False

class Message:
    def __init__(self, text, priority):
        self.text=text
        self.timestamp=time.time()
        self.priority = priority
        
everyone = {}
everything = []
priority = []
status_updates = False
        
def send_to_google_talk(msg):
    global cl
    name = everyone.keys()
    everything.append(Message(msg.text.rstrip(), msg.priority))
    print "adding_message " +msg.text+" with_priority " +msg.priority
    # output_file.flush()
    for k,v in everyone.items():
        if v.send_message(msg) and msg.text.find("<" +v.name+">" ) ==  - 1:
            print "send_message"
            # output_file.flush()
            if not cl.isConnected():
	        print "Reconnecting..."
                cl=xmpp.Client(jid.getDomain(),debug=[])
                try:
                    cl.connect((GTALK_SERVER,5222))
                except Exception:
                    print "connection_error"
                    # output_file.flush()
                try:
                    cl.auth(jid.getNode(),GMAIL_PASS)
                    cl.sendInitPresence()
                except Exception:
                    print "authentication_error"
                    # output_file.flush()
            try:
                send_gtalk(v.user,msg.text)
		print v.user
		print "message sent"
                v.timestamp = time.time()
            except Exception:
                print "send_error" 

                # output_file.flush()
        time.sleep(0.1)


def send_gtalk(to,msg):
    global cl
    cl.send( xmpp.protocol.Message(to =to,body=msg.encode('utf8'),typ='chat') )

def send_status_irc(sender):
    for k,v in everyone.items():
        text = k+": " 
        for name, status in v.show.items():
            text = text+" ("  + name + ") "  +status + ", '"  + v.status[name] + "'" 
            Asynchronous_IRC.send_to(sender, text) 
	    time.sleep(5)
        
        
def parsemsg(msg):
    print "mmessage: ",
    print msg
    complete=msg[1:].rsplit('PRIVMSG') #Parse the message into useful data
    print complete
    info=complete[0]
    print info
    msgpart=complete[1].split (':', 1) [1]
    print msgpart
    receiver = complete [1].split (':', 1) [0]
    print receiver
    sender=info.split('!')
    print sender
    print "Parsing msg: ",

  







    # output_file.flush()
    text = 0
    
    text = Message("<" +sender[0]+"> " +msgpart, "all" )
    if receiver.find(CHANNELINIT) != -1:
        send_to_google_talk(text) 
        if msgpart.find(NICK[0]+":") !=  - 1:
            texta = Message(sender[0] +": " +msgpart, "priority" )
            send_to_google_talk(texta)
    if msgpart[0]=="'":
        cmd=msgpart[1:].split(' ')
        if cmd[0] == 'status':
            send_status_irc(sender[0])
        if cmd[0] == 'skjera':
            send_log_irc(sender, cmd)
    if msgpart[0]=='`' and sender[0]==OWNER: #Treat all messages starting with '`' as command
        print "This is a command\n"
        cmd=msgpart[1:].split(' ')
        print "the command is " +str(cmd)
        # output_file.flush()
        if cmd[0]=='op':
            Asynchronous_IRC.transmit('MODE '+info[2]+' +o '+cmd[1]+'\r\n')
        if cmd[0]=='deop':
            Asynchronous_IRC.transmit('MODE '+info[2]+' -o '+cmd[1]+'\r\n')
        if cmd[0]=='voice':
            Asynchronous_IRC.transmit('MODE '+info[2]+' +v '+cmd[1]+'\r\n')
        if cmd[0]=='devoice':
            Asynchronous_IRC.transmit('MODE '+info[2]+' -v '+cmd[1]+'\r\n')
        if cmd[0]=='sys':
            syscmd(msgpart[1:],info[2])
        
    if msgpart[0]=='-' and sender[0]==OWNER : #Treat msgs with - as explicit command to send to server
        cmd=msgpart[1:]
        Asynchronous_IRC.transmit(cmd+'\r\n')
        print 'cmd='+cmd
        # output_file.flush()




class irc(asynchat.async_chat):
    
    def __init__(self):
        global start_time
        asynchat.async_chat.__init__(self)
        self.connected=False
	self.disconnected=False
        try:
            self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
            self.connect((HOST, PORT))
        except Exception:
            print "failed_connecting_to_irc_server"
            print Exception
            # output_file.flush()
            return None
	self.random_number = random.randint (1, 100)
	self.name = NICK +'_' + str(self.random_number)
        self.ibuffer = ''
        self.obuffer = ""
        self.set_terminator("\r\n")
        self.last=time.time()
        self.last_ping = time.time()
	self.last_data = time.time()
        self.ping_interval = -1
	start_time=time.time()

    def handle_connect(self):
        self.connected=True
	self.propagate('NICK '+ self.name +'\r\n') #Send the nick to server
        self.propagate('USER '+IDENT+' '+HOST+' bla :'+REALNAME+'\r\n') #Identify to server

    def handle_error(self):
	print "some error has occurred"
        #elf.connected=False
	#elf.disconnected=True

    def handle_close(self):
        self.connected=False

    def collect_incoming_data(self, data):
        """Buffer the data"""
        self.ibuffer=self.ibuffer+data

    def found_terminator(self):
        self.handle(str(self.ibuffer))
        self.ibuffer=''
        
    def handle(self,sentence):
        print "recieved from IRC: ",
        print sentence
	self.last_data = time.time()
	if self.ping_interval == -1:
	    self.ping_interval = 150
        if sentence.find(TRIGGERTEXT)!=-1: #This is Crap(I wasn't sure about it but it works)
            print "joining channel"
            self.propagate('JOIN '+CHANNELINIT+'\r\n') #Join a channel
            #self.push ( 'PRIVMSG '+CHANNELINIT+' :Hello.\r\n' )
            self.transmit ('PRIVMSG '+CHANNELINIT+" :I'm back\r\n" )
        if sentence.find('PRIVMSG')!=-1 and sentence.encode('utf8').find('VERSION') == -1: #Call a parsing function
            print "pricing message..."
            parsemsg (sentence)

        sentence=sentence.rstrip() #remove trailing 'rn'
        sentence=sentence.split()
        if(sentence[0]=='PING'): #If server pings then pong
            if self.last_ping > 0:
                self.ping_interval = time.time() - self.last_ping
            self.last_ping = time.time()
            print "ping" 
            # output_file.flush()
            self.propagate('PONG '+sentence[1]+'\r\n')

    def propagate(self, line):
        try:
            if self.connected:
                self.push(line)
            else:
                print "not connected_when_sending" 
        except Exception, exception:
            print "failed_to_send"
            print line
            print exception
            # output_file.flush()

    def transmit (self, line):
        print line
        # output_file.flush()
        parsemsg(":gt"+line)
        self.propagate (line)

    def send_to (self, target,line):
        msg = "PRIVMSG "+target+' :'+line.encode('utf8') +'\r\n'
        print "Sending msg to IRC: ",
        print msg
        # output_file.flush()
        parsemsg(":gt"+msg)
        self.propagate (msg)
        
def print_users():
    global everyone
    try:
        f = open('users.txt', 'w')
	print "open"
        for k, v in everyone.items():

	    f.write('%s\n' %(v.user))

            # output_file.flush()
        f.close()
    except Exception, e:
        print e
    f = open('users.txt', 'r')
    for relying in f.readlines():
        print relying
        # output_file.flush()
    
def new_user(name):
    if not name in everyone:
        everyone[name] = Member(name)
        print "we_have_added_user " +name
    # output_file.flush()
    #print_users()

def remove_user(name):
    if name in everyone:
        del everyone[name]
        print "we_have_deleted_use_of " +name
        # output_file.flush()
        print_users()

    else:
        print "uses " +name+" doesnot exist" 
        # output_file.flush()
    
def read_users():
    try:
        f=open('users.txt', 'r')
        for line in f.readlines() :

            line = line.strip()
            print line
            # output_file.flush()
            if len(line)  > 9:
                date = line.split(' ')
                everyone[date[0]] =Member(date[0])
                print pickle.dumps(everyone[date[0]])
                # output_file.flush()
        if len(everyone) ==0:
            everyone[admin] =Member(admin)
                
    except:
        print "they_ll_know_file" 
        # output_file.flush()
        everyone[admin] = Member(admin) 
        
def send_help (user):
    text = """help -- this message
    status -- e-mail with statuses
    skjera t <hour> -- log for given time(float)
    skjera l <number> -- log for given lines(int)"""
    send_gtalk (user, text)


def send_log_g_talk(sender, text):
    user = everyone[sender]
    now = user.timestamp
    priority = 'all'
    message_text = ''
    print text
    count = 0
    old_timestamp = user.timestamp
    user.timestamp = time.time()
    if len(text)  > 2:
        if text[1] == 't':
    	    try:
	        now = time.time() - (float(text[2]) * 3600)
	    except ValueError:
	        send_gtalk (user.user, "not a valid number")
		return
	    for msg in everything:
	        if msg.timestamp > now:
		    count = count + 1
	            message_text = message_text + "%s: %s\n" %(time.asctime(time.localtime(msg.timestamp)), msg.text)
	elif text[1] == 'l':
	    try:
	        tall = int(text[2])
	    except ValueError:
	        send_gtalk (user.user, "not a valid number")
		return
	    print "%i before adjustment"% (tall)
	    if tall > len(everything):
	        tall = len(everything)
	    print "%i after  adjustment"% (tall)
	    if tall == 0:
	        return
	    tall= -tall
	    for msg in everything[tall:]:
	        print "message text:" +msg.text
                count = count + 1
	        message_text = message_text + "%s: %s\n" %(time.asctime(time.localtime(msg.timestamp)), msg.text)
	    print message_text
	    print "count: %i" % (count)
    else:
        user.timestamp = time.time()
        count = 0
        for msg in everything:
            if msg.timestamp > old_timestampy:
                count = count + 1
                message_text = message_text + "%s: %s\n" %(time.asctime(time.localtime(msg.timestamp)), msg.text)
    if count == 0:
        text="Intet nytt."
        send_gtalk(user.user,text)
    else:
        msg = MIMEMultipart()
        msg['From'] = FROM_GMAIL_ID
        msg['To'] = user.user
        msg['Subject'] = "IRC log from %s" %( time.asctime(time.localtime(old_timestamp) ))
        msg.attach(MIMEText(message_text))
        print msg.as_string()
        # output_file.flush()
        try:
            smtpObj = smtplib.SMTP('smtp.gmail.com',587)
            smtpObj.set_debuglevel(1)
            smtpObj.ehlo()
            smtpObj.starttls()

            smtpObj.ehlo()
            smtpObj.login(FROM_GMAIL_ID, GMAIL_PASS)
            smtpObj.sendmail(FROM_GMAIL_ID, user.user, msg.as_string())
            smtpObj.close()
            print "Successfully sent email"
            # output_file.flush()
        except smtplib.SMTPException:
            print "Error: unable to send email"
	    # output_file.flush()
        print "all done"
        # output_file.flush()


def send_stat_g_talk(sender, textit):
    user = everyone[sender]
    text=""
    for k,v in everyone.items():
        text = text+k+": " 
        for name, status in v.show.items():
            text = text+" ("  + name + ") "  +status + ", '"  + v.status[name] + "'\n" 
	text=text+"\n\n"
#	print text
    msg = MIMEMultipart()
    msg['From'] = FROM_GMAIL_ID
    msg['To'] = user.user
    msg['Subject'] = "IRC status"
    msg.attach(MIMEText(text))
    print msg.as_string()
    # output_file.flush()
    try:
        smtpObj = smtplib.SMTP('smtp.gmail.com',587)
        smtpObj.set_debuglevel(1)
        smtpObj.ehlo()
        smtpObj.starttls()

        smtpObj.ehlo()
        smtpObj.login(FROM_GMAIL_ID, GMAIL_PASS)
        smtpObj.sendmail(FROM_GMAIL_ID, user.user, msg.as_string())
        smtpObj.close()
        print "Successfully sent email"
        # output_file.flush()
    except smtplib.SMTPException:
        print "Error: unable to send email"
        # output_file.flush()
    print "all done"
    # output_file.flush()

    
            
        
def presenceCB(sess,pres):
    global status_updates
    #print pres
    #everyone[str(pres.getFrom().getResource())].status=str(pres.getType())
    sender  =  str(pres.getFrom())
    name = sender.split('/') [0]
    type = show = 'available'
    status = ''
    if name != FROM_GMAIL_ID:
        new_user(name)
    if not pres.getShow() is None:
        show = str(pres.getShow())
    if not pres.getType() is None:
        show = str(pres.getType())
    if not pres.getStatus() is None:
        status = str(pres.getStatus()) 
    #print sender+": Show=" +show+" type= " +type + " status = "  + status
    if name in everyone:
        everyone[name].type[sender] = type
        if sender in everyone[name].show:
            older_show = everyone[name].show[sender]
            old_status = everyone[name].status[ sender ]
        else:
            older_show = ''
            old_status = ''
        everyone[name].show[sender] = show
        everyone[name].status[sender] = status
	everyone[name].show_time[sender] = time.time()
	times=everyone[name].show_time.keys()
	for id in times:
	    if everyone[name].show_time[id] +3600<time.time():
	        del(everyone[name].show_time[id])
		del(everyone[name].show[id])
		del(everyone[name].status[id])
        if status_updates and (  status != old_status) :
            if status.find("dnd" ) ==  - 1  and old_status.find("dnd" ) !=  - 1:
                Asynchronous_IRC.send_to(CHANNELINIT, "%s (%s) is available" %(name.split('@')[0], sender.split('/')[1]) )
            elif status.find("dnd" )  !=   - 1 and old_status.find('dnd') ==  - 1:
                Asynchronous_IRC.send_to(CHANNELINIT, "%s (%s) is away" %(name.split('@')[0], sender.split('/')[1]) )

def messageCB(conn,msg):
    global status_updates
    sender  =  str(msg.getFrom())
    sender = sender.split('/') [0]
    type = str(msg.getType()) 
    #print msg
    if type =='chat':
        text = msg.getBody()
        if not Asynchronous_IRC.connected:
            reply = "irc is disconnected" 
            send_gtalk(sender,reply)
            text = None
        if not text is None:
            text = text.split()
            if text[0].lower() == 'skjera':
                send_log_g_talk(sender, text)
	    elif text[0].lower() == 'status':
		print "status"
                # output_file.flush()
                send_stat_g_talk(sender, text)
            elif text[0].lower() == 'kill' and text[1].lower() == 'now':
                send_gtalk(sender,'Dieing...')
                Asynchronous_IRC.transmit ('PRIVMSG '+CHANNELINIT+' :Goodbye cruel world\r\n' )
                Asynchronous_IRC.close()
                #subprocess.popen(['python', 'hans.py'])
                cl.disconnect()
                exit(0)
	    elif text[0].lower() == 'help':
	         send_help (sender)
            #elif text[0].lower() == 'set':
            #    change_status(sender, text)
            elif text[0].lower() == 'add' and sender == admin:
                new_user(text[1])
            elif text[0].lower() == 'remove' and sender == admin:
                remove_user(text[1])
            elif text[0].lower() == 'toggle' and sender == admin:
                status_updates = not status_updates
                text = "False"
                if status_updates:
                    text = "True"
                text = "status_updates   = "  + text
                send_gtalk(sender,text)
            else:
                Asynchronous_IRC.transmit ('PRIVMSG '+CHANNELINIT+' :<'+sender.split('@')[0]+'> '+str(msg.getBody())+'\r\n' )

    else:
        print "error_message: ",
        print msg
        # output_file.flush()
        
def StepOn(conn):
    try:
        conn.Process(1)
    except KeyboardInterrupt:
        return 0
    except Exception:
        gtalk_connected=False
    return 1



reload(sys)
sys.setdefaultencoding( "latin-1" )


jid=xmpp.protocol.JID(FROM_GMAIL_ID)
cl=xmpp.Client(jid.getDomain(),debug=[])
if not cl.connect((GTALK_SERVER,5222)):
    raise IOError('Can not connect to server.')
if not cl.auth(jid.getNode(),GMAIL_PASS):
    raise IOError('Can not auth with server.')
gtalk_connected = True
cl.RegisterHandler('message', messageCB)
cl.RegisterHandler('presence',presenceCB)
#cl.sendInitPresence()

#read_users()
#s=socket.socket( ) #Create the socket


start_time=time.time()
#time.sleep (5)
Asynchronous_IRC =irc ()
last_presence = 0
while 1:
    if (Asynchronous_IRC== None or  Asynchronous_IRC.disconnected or (Asynchronous_IRC.ping_interval > 0 and time.time() - Asynchronous_IRC.last_data > Asynchronous_IRC.ping_interval * ping_timeout) or (Asynchronous_IRC.ping_interval==-1 and time.time()-start_time > 600)):
	if isinstance(Asynchronous_IRC, irc):
	    print "%s, ping_interval_is %d, last_ping_is %s, last data is %s" %(time.asctime(time.localtime()), Asynchronous_IRC.ping_interval, time.asctime(time.localtime(Asynchronous_IRC.last_ping)), time.asctime(time.localtime(Asynchronous_IRC.last_data)) )
            # output_file.flush()
            if Asynchronous_IRC.disconnected:
                Asynchronous_IRC.close()    
                print "IRC has been disconnected"
        else:
	    print "this IRC object has been killed"
            Asynchronous_IRC.close()
        print "reinit"
        # output_file.flush()
        Asynchronous_IRC =irc ()
	continue
    asyncore.poll(timeout = 0.1)
    if time.time() > last_presence + 150:
        print "%s, ping_interval_is %d, last_ping_is %s" %(time.asctime(time.localtime()), Asynchronous_IRC.ping_interval, time.asctime(time.localtime(Asynchronous_IRC.last_ping)) )
        # output_file.flush()
        try:
            send_gtalk(FROM_GMAIL_ID, "ping" )
        except Exception, exception:
            print "an_ascending"
            # output_file.flush()
	    print exception
            # output_file.flush()
            gtalk_connected = False
        print cl.isConnected()
        # output_file.flush()
        last_presence = time.time()
        if not gtalk_connected:
	    cl=None
            cl=xmpp.Client(jid.getDomain(),debug=[])
            try:
                cl.connect((GTALK_SERVER,5222))
            except Exception:
                print "connection_error"
                # output_file.flush()
            try:
                cl.auth(jid.getNode(),GMAIL_PASS)
            except Exception:
                print "authentication_error"
                # output_file.flush()
            gtalk_connected = True
	    cl.RegisterHandler('message', messageCB)
	    cl.RegisterHandler('presence',presenceCB)
	    print "gtalk connected"
            # output_file.flush()
        if cl.isConnected():
            cl.sendInitPresence()
        
    StepOn(cl)

cl.disconnect()

